﻿using CommonFP;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Text.RegularExpressions;

namespace CommonEngine
{
    /// <summary>
    /// 基础扩展
    /// </summary>
    public static partial class Extensions
    {
        /// <summary>
        /// 安全返回值
        /// </summary>
        public static T SafeValue<T>(this T? value) where T : struct
            => value ?? default(T);

        public static bool Equal<T>(this T x, T y)
            => FP.Try(() => ((IComparable)(x)).CompareTo(y) == 0).Run().Outcome();

        /// <summary>
        /// 对象是否为空
        /// </summary>
        public static bool IsNull(this object instance)
            => instance == null;

        /// <summary>
        /// 是否为空
        /// </summary>
        /// <param name="value">值</param>
        public static bool IsEmpty(this string value)
            => string.IsNullOrWhiteSpace(value);

        /// <summary>
        /// 是否为空
        /// </summary>
        /// <param name="value">值</param>
        public static bool IsEmpty(this Guid? value)
            => value == null ? true : IsEmpty(value.Value);

        /// <summary>
        /// 是否为空
        /// </summary>
        /// <param name="value">值</param>
        public static bool IsEmpty(this Guid value)
            => value == Guid.Empty;

        /// <summary>
        /// 是否为空
        /// </summary>
        /// <param name="value">值</param>
        public static bool IsEmpty(this object value)
            => !(value != null && !string.IsNullOrEmpty(value.ToString()));

        /// <summary>
        /// 判断集合是否存在数据
        /// </summary>
        public static bool IsNotNullOrEmpty<T>(this IEnumerable<T> enumerable)
            => (enumerable != null) && enumerable.Any<T>();

        /// <summary>
        /// 集合为空或长度为0
        /// </summary>
        public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable)
            => (enumerable == null) || !enumerable.Any<T>();

        /// <summary>
        /// 合并字符串去重集合
        /// </summary>
        public static Option<string> MergeDistinct(this IEnumerable<string> source, char c = ',')
        {
            if (source == null || !source.Any())
                return string.Empty;

            if (char.IsWhiteSpace(c))
                return string.Empty;

            List<string> ALL_ls = new List<string>();
            foreach (var v in source)
                ALL_ls.AddRange(v.Split(c).ToList());

            ALL_ls = ALL_ls.Distinct().ToList();    //去重
            string result = string.Empty;
            foreach (var v in ALL_ls)
                result += c + v;

            return result.Substring(1, result.Length - 1);
        }

        /// <summary>
        /// 合并字符串集合
        /// </summary>
        public static Option<string> Merge(this IEnumerable<string> source, char c = ',')
        {
            if (source == null || !source.Any())
                return string.Empty;

            if (char.IsWhiteSpace(c))
                return string.Empty;

            var sb = source.Aggregate(new StringBuilder(), (optRs, t) => optRs.Append(t + c));
            return sb.ToString().Substring(0, sb.Length - 1);
        }

        #region---字符串扩展---

        /// <summary>
        /// 字串是否在指定字串中存在(内存模糊查询)
        /// </summary>
        /// <param name="obj">字串</param>
        /// <param name="value">被包含字串</param>
        /// <returns></returns>
        public static bool HasLike(this string obj, string value)
            => !string.IsNullOrEmpty(value) &&
            !string.IsNullOrEmpty(obj) &&
            (obj.Length > PragmaticAction.LevenshteinDistance(obj, value));

        /// <summary>
        /// 是否包含
        /// </summary>
        public static bool HasContains(this string obj, string value)
            => !string.IsNullOrEmpty(value) &&
            !string.IsNullOrEmpty(obj) &&
            (value.IndexOf(obj) != -1);

        /// <summary>
        /// 压缩重复字段。只处理2个以上的重复字符。
        /// </summary>
        /// <param name="input">原始字符串</param>
        /// <param name="repeatChar">重复字符</param>
        /// <param name="targetString">替换字符串</param>
        /// <returns></returns>
        public static string CompressRepeatChar(this string source, char repeatChar, string targetString)
            => source != null ? Regex.Replace(source, repeatChar.ToString() + "{2,}", mt => targetString) : source;

        /// <summary>
        /// 截断字符串(be truncated)
        /// </summary>
        /// <param name="input">被截断字符串</param>
        /// <param name="trunLength">截断长度</param>
        /// <returns></returns>
        public static string TruncateString(this string source, int trunLength)
            => (source != null && source.Length > trunLength) ?
            source.Substring(0, (trunLength - 32)) + "\r\n\r\n...(be truncated)" : source;

        /// <summary>
        /// 截断字符串
        /// </summary>
        public static string MaxSubstring(this string origin, int maxLength, string truncated = null)
            => origin.Length >= maxLength ?
            origin.Substring(0, maxLength) + (string.IsNullOrWhiteSpace(truncated) ?
            "" : truncated) : origin;

        #endregion

        public static Option<string> Format(this object obj)
            => (obj is string || obj is char) ? obj.ToString() : JsonHelper.SerializeObjectToJson(obj);

        /// <summary>
        /// 返回RESTful输出字符串
        /// </summary>
        public static StringContent ToContent(this object obj)
            => obj.ToOption()
            .Bind(Format)
            .Map(a => new StringContent(a, Encoding.GetEncoding("UTF-8"), "application/json"))
            .Outcome();

        /// <summary>
        /// 深度复制
        /// </summary>
        public static Validation<T> DeepCopy<T>(this T RealObject) where T : class
             => (RealObject == null)
            ? FP.Invalid("RealObject Is Null.")
            : FP.Try(
                () =>
                {
                    //要复制的实例必须可序列化，包括实例引用的其它实例都必须在类定义时加[Serializable]特性。 
                    using (Stream objectStream = new MemoryStream())
                    {
                        //利用 System.Runtime.Serialization序列化与反序列化完成引用对象的复制
                        IFormatter formatter = new BinaryFormatter();
                        formatter.Serialize(objectStream, RealObject);
                        objectStream.Seek(0, SeekOrigin.Begin);
                        return (T)formatter.Deserialize(objectStream);
                    }
                }).Run().Match(e => FP.Invalid(e.Message), a => FP.Valid(a));
    }
}
